﻿// Hotkeys handling class, (c)2010 Knihovnik
// This file is licensed under GNU GPL license v2, see more at http://www.gnu.org/licenses/gpl-2.0.html
namespace vKapse.VirtualDesktop.Core
{

	#region Using

	using System;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.Runtime.InteropServices;
	using System.Windows.Forms;

	#endregion Using

	/// <summary>
	/// Class for HotKeys (hooking Win32 APIs)
	/// </summary>
	internal class HotKeyWrapper
	{

		#region Class HotKeyHelper

		/// <summary>
		/// 
		/// </summary>
		private class HotKeyHelper : Form
		{
			public HotKeyHelper() :
				base ()
			{
				this.Width = this.Height = 1;
				this.Visible = false;
				this.ShowInTaskbar = false;
				this.FormBorderStyle = FormBorderStyle.None;
			}

			protected override void WndProc(ref Message m)
			{
				if (m.Msg == HotKeyWrapper.WM_HOTKEY)
				{
					// Rebuild back Keys ;)
					Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
					int keyCode = m.LParam.ToInt32(); // Get keyCode
					// and OR modifier
					if ((keyCode & HotKeyWrapper.MOD_ALT) == HotKeyWrapper.MOD_ALT)
					{
						key = key | Keys.Alt;
					}
					if ((keyCode & HotKeyWrapper.MOD_CONTROL) == HotKeyWrapper.MOD_CONTROL)
					{
						key = key | Keys.Control;
					}
					if ((keyCode & HotKeyWrapper.MOD_SHIFT) == HotKeyWrapper.MOD_SHIFT)
					{
						key = key | Keys.Shift;
					}
					if (this.HotKeyPressed != null)
					{
						this.HotKeyPressed(this, new KeyEventArgs(key));
					}
				}
				base.WndProc(ref m);
			}

			public event KeyEventHandler HotKeyPressed;
		}

		#endregion Class HotKeyHelper

		#region Consts

		private static int MOD_ALT = 0x1;
		private static int MOD_CONTROL = 0x2;
		private static int MOD_SHIFT = 0x4;
		private static int WM_HOTKEY = 0x312;

		#endregion Consts

		#region Win32

		[DllImport("user32.dll")]
		private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);

		[DllImport("user32.dll")]
		private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

		#endregion Win32

		#region Members

		private static Dictionary<Keys, int> _hotkeys;
		private delegate void MethodInvoker();
		private static HotKeyHelper _helper;

		#endregion Members

		#region Constructor

		/// <summary>
		/// Static constructor
		/// </summary>
		static HotKeyWrapper()
		{
			_hotkeys = new Dictionary<Keys, int>();
			_helper = new HotKeyHelper();
			_helper.Show();
			_helper.Hide();
			_helper.HotKeyPressed += new KeyEventHandler(HotKeyWrapper.HotKeyPressedEventReaction);
		}

		#endregion Constructor

		#region Methods

		/// <summary>
		/// 
		/// </summary>
		public static void UnregisterAll()
		{
			foreach (Keys key in _hotkeys.Keys)
			{
				HotKeyWrapper.Unregister(key);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		public static void Unregister(Keys key)
		{
			try
			{
				MethodInvoker invoker = delegate()
				{
					int keyId = _hotkeys[key]; // Get key id
					HotKeyWrapper.UnregisterHotKey(_helper.Handle, keyId); // modify this if you want more than one hotkey
				};

				_helper.Invoke(invoker);
			}
			catch (Exception ex)
			{
				Debug.Write(ex);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		public static void Register(Keys key)
		{
			int m = 0;
			// Now we gonna check modifiers
			if ((key & Keys.Alt) == Keys.Alt)
				m = m | HotKeyWrapper.MOD_ALT;

			if ((key & Keys.Control) == Keys.Control)
				m = m | HotKeyWrapper.MOD_CONTROL;

			if ((key & Keys.Shift) == Keys.Shift)
				m = m | HotKeyWrapper.MOD_SHIFT;

			// AND the key
			Keys k = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt;

			MethodInvoker invoker = delegate()
			{
				int keyId = 1024 + (_hotkeys.Count + 1);
				_hotkeys.Add(key, keyId);
				HotKeyWrapper.RegisterHotKey(_helper.Handle, keyId, m, (int)k);
			};
			// Invoke (register)
			_helper.Invoke(invoker);
		}

		#endregion Methods

		#region Events

		/// <summary>
		/// 
		/// </summary>
		public static event KeyEventHandler HotKeyPressed;

		#endregion Events

		#region Event Reactions

		[DebuggerStepThrough]
		static void HotKeyPressedEventReaction(object sender, KeyEventArgs e)
		{
			if (HotKeyWrapper.HotKeyPressed != null)
			{
				HotKeyWrapper.HotKeyPressed(new object(), e);
			}
		}

		#endregion Event Reactions

	}
}
